Fixing popup menus to have "Paste" sensitized correctly. Original patches
authorOwen Taylor <otaylor@redhat.com>
Mon, 22 Oct 2001 20:47:01 +0000 (20:47 +0000)
committerOwen Taylor <otaylor@src.gnome.org>
Mon, 22 Oct 2001 20:47:01 +0000 (20:47 +0000)
Mon Oct 22 16:25:12 2001  Owen Taylor  <otaylor@redhat.com>

Fixing popup menus to have "Paste" sensitized correctly. Original
patches from Damian Ivereigh, much mangled.

* gtk/gtkselection.c: Add functions gtk_selection_data_get_targets(),
gtk_selection_data_targets_include_text(). (#60854)

* gtk/gtkclipboard.c: Add a simple do-it-all non-async "check if
the clipboard has text" function gtk_clipboard_wait_is_text_available.
(#60854)

* gtk/gtkentry.c: Only enable the paste item if the clipboard
contains text. (#60973)

* gtk/gtktextview.c: Only enable the paste item if the clipboard
contains text. (#60975)

13 files changed:
ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
gtk/gtkclipboard.c
gtk/gtkclipboard.h
gtk/gtkentry.c
gtk/gtkselection.c
gtk/gtkselection.h
gtk/gtktextview.c

index 6db2b8fc4f78d1b6da3aec5c75425c7fbea5eaa5..e0bc24e6690532c633cf000aef699c3fff088caa 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+Mon Oct 22 16:25:12 2001  Owen Taylor  <otaylor@redhat.com>
+
+       Fixing popup menus to have "Paste" sensitized correctly. Original
+       patches from Damian Ivereigh, much mangled.
+
+       * gtk/gtkselection.c: Add functions gtk_selection_data_get_targets(),
+       gtk_selection_data_targets_include_text(). (#60854)
+
+       * gtk/gtkclipboard.c: Add a simple do-it-all non-async "check if
+       the clipboard has text" function gtk_clipboard_wait_is_text_available.
+       (#60854)
+
+       * gtk/gtkentry.c: Only enable the paste item if the clipboard
+       contains text. (#60973)
+
+       * gtk/gtktextview.c: Only enable the paste item if the clipboard
+       contains text. (#60975)
+
 2001-10-22  Havoc Pennington  <hp@redhat.com>
 
        * gtk/gtktextview.c (gtk_text_view_class_init): rip out
index 6db2b8fc4f78d1b6da3aec5c75425c7fbea5eaa5..e0bc24e6690532c633cf000aef699c3fff088caa 100644 (file)
@@ -1,3 +1,21 @@
+Mon Oct 22 16:25:12 2001  Owen Taylor  <otaylor@redhat.com>
+
+       Fixing popup menus to have "Paste" sensitized correctly. Original
+       patches from Damian Ivereigh, much mangled.
+
+       * gtk/gtkselection.c: Add functions gtk_selection_data_get_targets(),
+       gtk_selection_data_targets_include_text(). (#60854)
+
+       * gtk/gtkclipboard.c: Add a simple do-it-all non-async "check if
+       the clipboard has text" function gtk_clipboard_wait_is_text_available.
+       (#60854)
+
+       * gtk/gtkentry.c: Only enable the paste item if the clipboard
+       contains text. (#60973)
+
+       * gtk/gtktextview.c: Only enable the paste item if the clipboard
+       contains text. (#60975)
+
 2001-10-22  Havoc Pennington  <hp@redhat.com>
 
        * gtk/gtktextview.c (gtk_text_view_class_init): rip out
index 6db2b8fc4f78d1b6da3aec5c75425c7fbea5eaa5..e0bc24e6690532c633cf000aef699c3fff088caa 100644 (file)
@@ -1,3 +1,21 @@
+Mon Oct 22 16:25:12 2001  Owen Taylor  <otaylor@redhat.com>
+
+       Fixing popup menus to have "Paste" sensitized correctly. Original
+       patches from Damian Ivereigh, much mangled.
+
+       * gtk/gtkselection.c: Add functions gtk_selection_data_get_targets(),
+       gtk_selection_data_targets_include_text(). (#60854)
+
+       * gtk/gtkclipboard.c: Add a simple do-it-all non-async "check if
+       the clipboard has text" function gtk_clipboard_wait_is_text_available.
+       (#60854)
+
+       * gtk/gtkentry.c: Only enable the paste item if the clipboard
+       contains text. (#60973)
+
+       * gtk/gtktextview.c: Only enable the paste item if the clipboard
+       contains text. (#60975)
+
 2001-10-22  Havoc Pennington  <hp@redhat.com>
 
        * gtk/gtktextview.c (gtk_text_view_class_init): rip out
index 6db2b8fc4f78d1b6da3aec5c75425c7fbea5eaa5..e0bc24e6690532c633cf000aef699c3fff088caa 100644 (file)
@@ -1,3 +1,21 @@
+Mon Oct 22 16:25:12 2001  Owen Taylor  <otaylor@redhat.com>
+
+       Fixing popup menus to have "Paste" sensitized correctly. Original
+       patches from Damian Ivereigh, much mangled.
+
+       * gtk/gtkselection.c: Add functions gtk_selection_data_get_targets(),
+       gtk_selection_data_targets_include_text(). (#60854)
+
+       * gtk/gtkclipboard.c: Add a simple do-it-all non-async "check if
+       the clipboard has text" function gtk_clipboard_wait_is_text_available.
+       (#60854)
+
+       * gtk/gtkentry.c: Only enable the paste item if the clipboard
+       contains text. (#60973)
+
+       * gtk/gtktextview.c: Only enable the paste item if the clipboard
+       contains text. (#60975)
+
 2001-10-22  Havoc Pennington  <hp@redhat.com>
 
        * gtk/gtktextview.c (gtk_text_view_class_init): rip out
index 6db2b8fc4f78d1b6da3aec5c75425c7fbea5eaa5..e0bc24e6690532c633cf000aef699c3fff088caa 100644 (file)
@@ -1,3 +1,21 @@
+Mon Oct 22 16:25:12 2001  Owen Taylor  <otaylor@redhat.com>
+
+       Fixing popup menus to have "Paste" sensitized correctly. Original
+       patches from Damian Ivereigh, much mangled.
+
+       * gtk/gtkselection.c: Add functions gtk_selection_data_get_targets(),
+       gtk_selection_data_targets_include_text(). (#60854)
+
+       * gtk/gtkclipboard.c: Add a simple do-it-all non-async "check if
+       the clipboard has text" function gtk_clipboard_wait_is_text_available.
+       (#60854)
+
+       * gtk/gtkentry.c: Only enable the paste item if the clipboard
+       contains text. (#60973)
+
+       * gtk/gtktextview.c: Only enable the paste item if the clipboard
+       contains text. (#60975)
+
 2001-10-22  Havoc Pennington  <hp@redhat.com>
 
        * gtk/gtktextview.c (gtk_text_view_class_init): rip out
index 6db2b8fc4f78d1b6da3aec5c75425c7fbea5eaa5..e0bc24e6690532c633cf000aef699c3fff088caa 100644 (file)
@@ -1,3 +1,21 @@
+Mon Oct 22 16:25:12 2001  Owen Taylor  <otaylor@redhat.com>
+
+       Fixing popup menus to have "Paste" sensitized correctly. Original
+       patches from Damian Ivereigh, much mangled.
+
+       * gtk/gtkselection.c: Add functions gtk_selection_data_get_targets(),
+       gtk_selection_data_targets_include_text(). (#60854)
+
+       * gtk/gtkclipboard.c: Add a simple do-it-all non-async "check if
+       the clipboard has text" function gtk_clipboard_wait_is_text_available.
+       (#60854)
+
+       * gtk/gtkentry.c: Only enable the paste item if the clipboard
+       contains text. (#60973)
+
+       * gtk/gtktextview.c: Only enable the paste item if the clipboard
+       contains text. (#60975)
+
 2001-10-22  Havoc Pennington  <hp@redhat.com>
 
        * gtk/gtktextview.c (gtk_text_view_class_init): rip out
index 6db2b8fc4f78d1b6da3aec5c75425c7fbea5eaa5..e0bc24e6690532c633cf000aef699c3fff088caa 100644 (file)
@@ -1,3 +1,21 @@
+Mon Oct 22 16:25:12 2001  Owen Taylor  <otaylor@redhat.com>
+
+       Fixing popup menus to have "Paste" sensitized correctly. Original
+       patches from Damian Ivereigh, much mangled.
+
+       * gtk/gtkselection.c: Add functions gtk_selection_data_get_targets(),
+       gtk_selection_data_targets_include_text(). (#60854)
+
+       * gtk/gtkclipboard.c: Add a simple do-it-all non-async "check if
+       the clipboard has text" function gtk_clipboard_wait_is_text_available.
+       (#60854)
+
+       * gtk/gtkentry.c: Only enable the paste item if the clipboard
+       contains text. (#60973)
+
+       * gtk/gtktextview.c: Only enable the paste item if the clipboard
+       contains text. (#60975)
+
 2001-10-22  Havoc Pennington  <hp@redhat.com>
 
        * gtk/gtktextview.c (gtk_text_view_class_init): rip out
index df2ef1d3d082319c81a6c4936019e03255d7ac63..655623173371c9931ab0197e3a9c175221b265cd 100644 (file)
@@ -820,3 +820,35 @@ gtk_clipboard_wait_for_text (GtkClipboard *clipboard)
   return results.data;
 }
 
+/**
+ * gtk_clipboard_wait_is_text_available:
+ * @clipboard: a #GtkClipboard
+ * 
+ * Test to see if there is text available to be pasted
+ * This is done by requesting the TARGETS atom and checking
+ * if it contains any of the names: STRING, TEXT, COMPOUND_TEXT,
+ * UTF8_STRING. This function waits for the data to be received
+ * using the main loop, so events, timeouts, etc, may be dispatched
+ * during the wait.
+ *
+ * This function is a little faster than calling
+ * gtk_clipboard_wait_for_text() since it doesn't need to retrieve
+ * the actual text.
+ * 
+ * Return value: %TRUE is there is text available, %FALSE otherwise.
+ **/
+gboolean
+gtk_clipboard_wait_is_text_available (GtkClipboard *clipboard)
+{
+  GtkSelectionData *data;
+  gboolean result = FALSE;
+
+  data = gtk_clipboard_wait_for_contents (clipboard, gdk_atom_intern ("TARGETS", FALSE));
+  if (data)
+    {
+      result = gtk_selection_data_targets_include_text (data);
+      gtk_selection_data_free (data);
+    }
+
+  return result;
+}
index d7ed47c9874118c6a6a496d1648b69a711790c7b..94a9bb0d5ccfc7d9c1a7ad8d46010f146b844252 100644 (file)
@@ -30,12 +30,12 @@ extern "C" {
 
 typedef struct _GtkClipboard GtkClipboard;
 
-typedef void (* GtkClipboardReceivedFunc)     (GtkClipboard     *clipboard,
-                                              GtkSelectionData *selection_data,
-                                              gpointer          data);
-typedef void (* GtkClipboardTextReceivedFunc) (GtkClipboard     *clipboard,
-                                              const gchar      *text,
-                                              gpointer          data);
+typedef void (* GtkClipboardReceivedFunc)        (GtkClipboard     *clipboard,
+                                                 GtkSelectionData *selection_data,
+                                                 gpointer          data);
+typedef void (* GtkClipboardTextReceivedFunc)    (GtkClipboard     *clipboard,
+                                                 const gchar      *text,
+                                                 gpointer          data);
 
 /* Should these functions have GtkClipboard *clipboard as the first argument?
  * right now for ClearFunc, you may have trouble determining _which_ clipboard
@@ -68,18 +68,20 @@ void     gtk_clipboard_set_text       (GtkClipboard          *clipboard,
                                       const gchar           *text,
                                       gint                   len);
 
-void gtk_clipboard_request_contents (GtkClipboard                 *clipboard,
-                                    GdkAtom                       target,
-                                    GtkClipboardReceivedFunc      callback,
-                                    gpointer                      user_data);
-void gtk_clipboard_request_text     (GtkClipboard                 *clipboard,
-                                    GtkClipboardTextReceivedFunc  callback,
-                                    gpointer                      user_data);
+void gtk_clipboard_request_contents (GtkClipboard                    *clipboard,
+                                    GdkAtom                          target,
+                                    GtkClipboardReceivedFunc         callback,
+                                    gpointer                         user_data);
+void gtk_clipboard_request_text     (GtkClipboard                    *clipboard,
+                                    GtkClipboardTextReceivedFunc     callback,
+                                    gpointer                         user_data);
 
 GtkSelectionData *gtk_clipboard_wait_for_contents (GtkClipboard *clipboard,
                                                   GdkAtom       target);
 gchar *           gtk_clipboard_wait_for_text     (GtkClipboard *clipboard);
 
+gboolean gtk_clipboard_wait_is_text_available   (GtkClipboard         *clipboard);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
index 503cd42b45b5f32bb90a52678e1b4ad5a16320b5..d993ad1d88bec7e19de807265268a964e0e079f0 100644 (file)
@@ -3721,7 +3721,7 @@ popup_position_func (GtkMenu   *menu,
   
   entry = GTK_ENTRY (user_data);  
   widget = GTK_WIDGET (entry);
-  
+
   g_return_if_fail (GTK_WIDGET_REALIZED (entry));
 
   gdk_window_get_origin (widget->window, x, y);      
@@ -3735,65 +3735,109 @@ popup_position_func (GtkMenu   *menu,
   *y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height));
 }
 
-static void
-gtk_entry_do_popup (GtkEntry       *entry,
-                    GdkEventButton *event)
+typedef struct
 {
+  GtkEntry *entry;
+  gint button;
+  guint time;
+} PopupInfo;
 
-  GtkWidget *menuitem;
-  GtkWidget *submenu;
-  gboolean have_selection;
-
-  if (entry->popup_menu)
-    gtk_widget_destroy (entry->popup_menu);
+static void
+popup_targets_received (GtkClipboard     *clipboard,
+                       GtkSelectionData *data,
+                       gpointer          user_data)
+{
+  PopupInfo *info = user_data;
+  GtkEntry *entry = info->entry;
   
-  entry->popup_menu = gtk_menu_new ();
-
-  gtk_menu_attach_to_widget (GTK_MENU (entry->popup_menu),
-                             GTK_WIDGET (entry),
-                             popup_menu_detach);
-
-  have_selection = entry->current_pos != entry->selection_bound;
+  if (GTK_WIDGET_REALIZED (entry))
+    {
+      gboolean clipboard_contains_text = gtk_selection_data_targets_include_text (data);
+      GtkWidget *menuitem;
+      GtkWidget *submenu;
+      
+      if (entry->popup_menu)
+       gtk_widget_destroy (entry->popup_menu);
+      
+      entry->popup_menu = gtk_menu_new ();
+      
+      gtk_menu_attach_to_widget (GTK_MENU (entry->popup_menu),
+                                GTK_WIDGET (entry),
+                                popup_menu_detach);
+      
+      append_action_signal (entry, entry->popup_menu, _("Cut"), "cut_clipboard",
+                           entry->editable && entry->current_pos != entry->selection_bound);
+      append_action_signal (entry, entry->popup_menu, _("Copy"), "copy_clipboard",
+                           entry->current_pos != entry->selection_bound);
+      append_action_signal (entry, entry->popup_menu, _("Paste"), "paste_clipboard",
+                           entry->editable && clipboard_contains_text);
+      
+      menuitem = gtk_menu_item_new_with_label (_("Select All"));
+      gtk_signal_connect_object (GTK_OBJECT (menuitem), "activate",
+                                GTK_SIGNAL_FUNC (gtk_entry_select_all), entry);
+      gtk_widget_show (menuitem);
+      gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem);
+      
+      menuitem = gtk_separator_menu_item_new ();
+      gtk_widget_show (menuitem);
+      gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem);
+      
+      menuitem = gtk_menu_item_new_with_label (_("Input Methods"));
+      gtk_widget_show (menuitem);
+      submenu = gtk_menu_new ();
+      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
+      
+      gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem);
+      
+      gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (entry->im_context),
+                                           GTK_MENU_SHELL (submenu));
+      
+      gtk_signal_emit (GTK_OBJECT (entry),
+                      signals[POPULATE_POPUP],
+                      entry->popup_menu);
   
-  append_action_signal (entry, entry->popup_menu, _("Cut"), "cut_clipboard",
-                        have_selection);
-  append_action_signal (entry, entry->popup_menu, _("Copy"), "copy_clipboard",
-                        have_selection);
-  append_action_signal (entry, entry->popup_menu, _("Paste"), "paste_clipboard",
-                        TRUE);
-
-  menuitem = gtk_menu_item_new_with_label (_("Select All"));
-  gtk_signal_connect_object (GTK_OBJECT (menuitem), "activate",
-                            GTK_SIGNAL_FUNC (gtk_entry_select_all), entry);
-  gtk_widget_show (menuitem);
-  gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem);
 
-  menuitem = gtk_separator_menu_item_new ();
-  gtk_widget_show (menuitem);
-  gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem);
-      
-  menuitem = gtk_menu_item_new_with_label (_("Input Methods"));
-  gtk_widget_show (menuitem);
-  submenu = gtk_menu_new ();
-  gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
+      if (info->button)
+       gtk_menu_popup (GTK_MENU (entry->popup_menu), NULL, NULL,
+                       NULL, NULL,
+                       info->button, info->time);
+      else
+       gtk_menu_popup (GTK_MENU (entry->popup_menu), NULL, NULL,
+                       popup_position_func, entry,
+                       info->button, info->time);
+    }
 
-  gtk_menu_shell_append (GTK_MENU_SHELL (entry->popup_menu), menuitem);
-      
-  gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (entry->im_context),
-                                        GTK_MENU_SHELL (submenu));
+  g_object_unref (entry);
+  g_free (info);
+}
+                       
+static void
+gtk_entry_do_popup (GtkEntry       *entry,
+                    GdkEventButton *event)
+{
+  PopupInfo *info = g_new (PopupInfo, 1);
 
-  gtk_signal_emit (GTK_OBJECT (entry),
-                   signals[POPULATE_POPUP],
-                   entry->popup_menu);
+  /* In order to know what entries we should make sensitive, we
+   * ask for the current targets of the clipboard, and when
+   * we get them, then we actually pop up the menu.
+   */
+  info->entry = g_object_ref (entry);
   
   if (event)
-    gtk_menu_popup (GTK_MENU (entry->popup_menu), NULL, NULL,
-                    NULL, NULL,
-                    event->button, event->time);
+    {
+      info->button = event->button;
+      info->time = event->time;
+    }
   else
-    gtk_menu_popup (GTK_MENU (entry->popup_menu), NULL, NULL,
-                    popup_position_func, entry,
-                    0, gtk_get_current_event_time ());
+    {
+      info->button = 0;
+      info->time = gtk_get_current_event_time ();
+    }
+
+  gtk_clipboard_request_contents (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
+                                 gdk_atom_intern ("TARGETS", FALSE),
+                                 popup_targets_received,
+                                 info);
 }
 
 static void
index 5b7d391607d3d94e66fc3563525d51727a77a877..21bcb510be6d3bc89395088e71d2cab2d32031d7 100644 (file)
@@ -866,6 +866,84 @@ gtk_selection_data_get_text (GtkSelectionData *selection_data)
   return result;
 }
 
+/**
+ * gtk_selection_data_get_targets:
+ * @selection_data: a #GtkSelectionData object
+ * @targets: location to store an array of targets. The result
+ *           stored here must be freed with g_free().
+ * @n_atoms: location to store number of items in @targets.
+ * 
+ * Get the contents of @selection_data as an array of targets.
+ * This can be used to interpret the results of getting
+ * the standard TARGETS target that is always supplied for
+ * any selection.
+ * 
+ * Return value: %TRUE if @selection_data contains a valid
+ *    array of targets, otherwise %FALSE.
+ **/
+gboolean
+gtk_selection_data_get_targets (GtkSelectionData  *selection_data,
+                               GdkAtom          **targets,
+                               gint              *n_atoms)
+{
+  if (selection_data->length >= 0 &&
+      selection_data->format == 32 &&
+      selection_data->type == GDK_SELECTION_TYPE_ATOM)
+    {
+      if (targets)
+       *targets = g_memdup (selection_data->data, selection_data->length);
+      if (n_atoms)
+       *n_atoms = selection_data->length / sizeof (GdkAtom);
+
+      return TRUE;
+    }
+  else
+    {
+      if (targets)
+       *targets = NULL;
+      if (n_atoms)
+       *n_atoms = -1;
+
+      return FALSE;
+    }
+}
+
+/**
+ * gtk_selection_data_targets_include_text:
+ * @selection_data: a #GtkSelectionData object
+ * 
+ * Given a #GtkSelectionData object holding a list of targets,
+ * Determines if any of the targets in @targets can be used to
+ * provide text.
+ * 
+ * Return value: %TRUE if @selection_data holds a list of targets,
+ *   and a suitable target for text is included, otherwise %FALSE.
+ **/
+gboolean
+gtk_selection_data_targets_include_text (GtkSelectionData *selection_data)
+{
+  GdkAtom *targets;
+  gint n_targets;
+  gint i;
+  gboolean result = FALSE;
+
+  if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
+    {
+      for (i=0; i < n_targets; i++)
+       {
+         if (targets[i] == gdk_atom_intern ("STRING", FALSE) ||
+             targets[i] == gdk_atom_intern ("TEXT", FALSE) ||
+             targets[i] == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
+             targets[i] == gdk_atom_intern ("UTF8_STRING", FALSE))
+           result = TRUE;
+       }
+
+      g_free (targets);
+    }
+
+  return result;
+}
+         
 /*************************************************************
  * gtk_selection_init:
  *     Initialize local variables
index 04a4ff3c56325fda216ebe3f42c5fa511119bf43..cf6b887a871b87f0c52f760089b7e50e76760447 100644 (file)
@@ -129,6 +129,11 @@ gboolean gtk_selection_data_set_text (GtkSelectionData     *selection_data,
                                      const guchar         *str);
 guchar * gtk_selection_data_get_text (GtkSelectionData     *selection_data);
 
+gboolean gtk_selection_data_get_targets          (GtkSelectionData  *selection_data,
+                                                 GdkAtom          **targets,
+                                                 gint              *n_atoms);
+gboolean gtk_selection_data_targets_include_text (GtkSelectionData  *selection_data);
+
 /* Called when a widget is destroyed */
 
 void gtk_selection_remove_all      (GtkWidget *widget);
index 1f7a8e4f905a51f3235a81d65d0f90611beb1cd4..a62681ef6acc53dd2d75733b3d9d3d7021d841f8 100644 (file)
@@ -5396,67 +5396,116 @@ popup_position_func (GtkMenu   *menu,
   *y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height));
 }
 
-static void
-gtk_text_view_do_popup (GtkTextView    *text_view,
-                        GdkEventButton *event)
+typedef struct
 {
-  GtkWidget *menuitem;
-  GtkWidget *submenu;
-  gboolean have_selection;
-  gboolean can_insert;
-  GtkTextIter iter;
-  
-  if (text_view->popup_menu)
-    gtk_widget_destroy (text_view->popup_menu);
-  
-  text_view->popup_menu = gtk_menu_new ();
-
-  gtk_menu_attach_to_widget (GTK_MENU (text_view->popup_menu),
-                             GTK_WIDGET (text_view),
-                             popup_menu_detach);
-
-  have_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
-                                                         NULL, NULL);
+  GtkTextView *text_view;
+  gint button;
+  guint time;
+} PopupInfo;
 
-  gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
-                                    &iter,
-                                    gtk_text_buffer_get_insert (get_buffer (text_view)));
-  
-  can_insert = gtk_text_iter_can_insert (&iter, text_view->editable);
+static void
+popup_targets_received (GtkClipboard     *clipboard,
+                       GtkSelectionData *data,
+                       gpointer          user_data)
+{
+  PopupInfo *info = user_data;
+  GtkTextView *text_view = info->text_view;
   
-  append_action_signal (text_view, text_view->popup_menu, _("Cut"), "cut_clipboard",
-                        have_selection);
-  append_action_signal (text_view, text_view->popup_menu, _("Copy"), "copy_clipboard",
-                        have_selection);
+  if (GTK_WIDGET_REALIZED (text_view))
+    {
+      /* We implicitely rely here on the fact that if we are pasting ourself, we'll
+       * have text targets as well as the private GTK_TEXT_BUFFER_CONTENTS target.
+       */
+      gboolean clipboard_contains_text = gtk_selection_data_targets_include_text (data);
+      GtkWidget *menuitem;
+      GtkWidget *submenu;
+      gboolean have_selection;
+      gboolean can_insert;
+      GtkTextIter iter;
 
-  append_action_signal (text_view, text_view->popup_menu, _("Paste"), "paste_clipboard",
-                        can_insert);
+      if (text_view->popup_menu)
+       gtk_widget_destroy (text_view->popup_menu);
 
-  menuitem = gtk_separator_menu_item_new ();
-  gtk_widget_show (menuitem);
-  gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem);
+      text_view->popup_menu = gtk_menu_new ();
       
-  menuitem = gtk_menu_item_new_with_label (_("Input Methods"));
-  gtk_widget_show (menuitem);
-  submenu = gtk_menu_new ();
-  gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
-  gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem);
+      gtk_menu_attach_to_widget (GTK_MENU (text_view->popup_menu),
+                                GTK_WIDGET (text_view),
+                                popup_menu_detach);
+      
+      have_selection = gtk_text_buffer_get_selection_bounds (get_buffer (text_view),
+                                                            NULL, NULL);
+      
+      gtk_text_buffer_get_iter_at_mark (get_buffer (text_view),
+                                       &iter,
+                                       gtk_text_buffer_get_insert (get_buffer (text_view)));
+      
+      can_insert = gtk_text_iter_can_insert (&iter, text_view->editable);
+      
+      append_action_signal (text_view, text_view->popup_menu, _("Cut"), "cut_clipboard",
+                           have_selection);
+      append_action_signal (text_view, text_view->popup_menu, _("Copy"), "copy_clipboard",
+                           have_selection);
+      append_action_signal (text_view, text_view->popup_menu, _("Paste"), "paste_clipboard",
+                           can_insert && clipboard_contains_text);
+      
+      menuitem = gtk_separator_menu_item_new ();
+      gtk_widget_show (menuitem);
+      gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem);
+      
+      menuitem = gtk_menu_item_new_with_label (_("Input Methods"));
+      gtk_widget_show (menuitem);
+      submenu = gtk_menu_new ();
+      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
+      gtk_menu_shell_append (GTK_MENU_SHELL (text_view->popup_menu), menuitem);
+      
+      gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (text_view->im_context),
+                                           GTK_MENU_SHELL (submenu));
+      
+      gtk_signal_emit (GTK_OBJECT (text_view),
+                      signals[POPULATE_POPUP],
+                      text_view->popup_menu);
+      
+      if (info->button)
+       gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL,
+                       NULL, NULL,
+                       info->button, info->time);
+      else
+       gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL,
+                       popup_position_func, text_view,
+                       0, gtk_get_current_event_time ());
+    }
+
+  g_object_unref (text_view);
+  g_free (info);
+}
 
-  gtk_im_multicontext_append_menuitems (GTK_IM_MULTICONTEXT (text_view->im_context),
-                                        GTK_MENU_SHELL (submenu));
+static void
+gtk_text_view_do_popup (GtkTextView    *text_view,
+                        GdkEventButton *event)
+{
+  PopupInfo *info = g_new (PopupInfo, 1);
 
-  gtk_signal_emit (GTK_OBJECT (text_view),
-                   signals[POPULATE_POPUP],
-                   text_view->popup_menu);
+  /* In order to know what entries we should make sensitive, we
+   * ask for the current targets of the clipboard, and when
+   * we get them, then we actually pop up the menu.
+   */
+  info->text_view = g_object_ref (text_view);
   
   if (event)
-    gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL,
-                    NULL, NULL,
-                    event->button, event->time);
+    {
+      info->button = event->button;
+      info->time = event->time;
+    }
   else
-    gtk_menu_popup (GTK_MENU (text_view->popup_menu), NULL, NULL,
-                    popup_position_func, text_view,
-                    0, gtk_get_current_event_time ());
+    {
+      info->button = 0;
+      info->time = gtk_get_current_event_time ();
+    }
+
+  gtk_clipboard_request_contents (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD),
+                                 gdk_atom_intern ("TARGETS", FALSE),
+                                 popup_targets_received,
+                                 info);
 }
 
 static void